package com.kin.bitmapcatching;

import java.io.IOException;
import java.lang.ref.WeakReference;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.media.ExifInterface;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.ImageView;

public class DisplayBitmap
{
	public interface ObserveCatching
	{
		public void addBitmapToMemCache(String key, Bitmap bitmap);
		public Bitmap getBitmapFromMemCache(String key);
		public Bitmap getBitmapFromDiskCache(String key);
	}

	private ObserveCatching mObserver;

	public DisplayBitmap(ObserveCatching observer)
	{
		mObserver = observer;
	}
	
	public class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap>
	{

		private final WeakReference imageViewReference;
		public String fileName;
		private Context mContext;

		public BitmapWorkerTask(ImageView imageView, Context context)
		{
			imageViewReference = new WeakReference(imageView);
			mContext = context;
		}

		@Override
		protected Bitmap doInBackground(String... params)
		{
			// TODO Auto-generated method stub
			fileName = params[0];
			Bitmap result = DisplayBitmap.decodeFromFileAndRolationDefendExif(mContext, fileName, 100, 100);
			return result;
		}

		@Override
		protected void onPostExecute(Bitmap result)
		{
			// TODO Auto-generated method stub
			if (isCancelled())
			{
				result = null;
			}
			if (imageViewReference != null && result != null)
			{
				final ImageView imageView = (ImageView) imageViewReference.get();
				final BitmapWorkerTask task = getBitmapWorkerTask(imageView);
				if (this == task && imageView != null)
				{
					imageView.setImageBitmap(result);
				}
			}
		}
	}
	
	public class BitmapWorkerWithMemCacheTask extends AsyncTask<String, Void, Bitmap>
	{

		private final WeakReference imageViewReference;
		public String fileImageUrl;
		private Context mContext;

		public BitmapWorkerWithMemCacheTask(ImageView imageView, Context context)
		{
			imageViewReference = new WeakReference(imageView);
			mContext = context;
		}

		@Override
		protected Bitmap doInBackground(String... params)
		{
			// TODO Auto-generated method stub
			fileImageUrl = params[0];
			String key = String.valueOf(fileImageUrl);
			Bitmap result = mObserver.getBitmapFromDiskCache(key);
			if (result == null)
			{
				result = DisplayBitmap.decodeFromFileAndRolationDefendExif(mContext, fileImageUrl, 100, 100);
				mObserver.addBitmapToMemCache(String.valueOf(fileImageUrl), result);
			}

			return result;
		}

		@Override
		protected void onPostExecute(Bitmap result)
		{
			// TODO Auto-generated method stub
			if (isCancelled())
			{
				result = null;
			}
			if (imageViewReference != null && result != null)
			{
				final ImageView imageView = (ImageView) imageViewReference.get();
				if (imageView != null)
				{
					imageView.setImageBitmap(result);
				}
			}
		}
	}

	/*
	 * Static
	 */
	public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight)
	{
		// Raw height and width of image
		final int height = options.outHeight;
		final int width = options.outWidth;
		int inSampleSize = 1;

		if (height > reqHeight || width > reqWidth)
		{
			if (width > height)
			{
				inSampleSize = Math.round((float) height / (float) reqHeight);
			}
			else
			{
				inSampleSize = Math.round((float) width / (float) reqWidth);
			}
		}
		return inSampleSize;
	}
	public static Bitmap decodeBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight)
	{

		// First decode with inJustDecodeBounds=true to check dimensions
		final BitmapFactory.Options options = new BitmapFactory.Options();
		options.inJustDecodeBounds = true;
		BitmapFactory.decodeResource(res, resId, options);

		// Calculate inSampleSize
		options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

		// Decode bitmap with inSampleSize set
		options.inJustDecodeBounds = false;
		return BitmapFactory.decodeResource(res, resId, options);
	}
	public static Bitmap decodeBitmapFromFile(String fileName, int reqWidth, int reqHeight)
	{

		// First decode with inJustDecodeBounds=true to check dimensions
		final BitmapFactory.Options options = new BitmapFactory.Options();
		options.inJustDecodeBounds = true;
		BitmapFactory.decodeFile(fileName, options);

		// Calculate inSampleSize
		options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

		// Decode bitmap with inSampleSize set
		options.inJustDecodeBounds = false;
		return BitmapFactory.decodeFile(fileName, options);
	}

	public static Bitmap rolateBitmap(Context mContext, Bitmap myBitmap)
	{
		// scale it to fit the screen, x and y swapped because my image is wider than it is tall
		Bitmap scaledBitmap = Bitmap.createScaledBitmap(myBitmap, myBitmap.getHeight(), myBitmap.getWidth(), true);

		// create a matrix object
		Matrix matrix = new Matrix();
		matrix.postRotate(90); // anti-clockwise by 90 degrees
		// create a new bitmap from the original using the matrix to transform the result
		Bitmap rotatedBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0, myBitmap.getHeight(), myBitmap.getWidth(),
				matrix, true);
		scaledBitmap.recycle();
		scaledBitmap = Bitmap.createScaledBitmap(rotatedBitmap, myBitmap.getHeight(), myBitmap.getWidth(), true);
		rotatedBitmap.recycle();
		return scaledBitmap;
	}

	public static Bitmap decodeFromFileAndRolationDefendExif(Context mContext, String pathFile, int reqWidth,
			int reqHeight)
	{
		ExifInterface exif;
		String rolation = "1";
		try
		{
			exif = new ExifInterface(pathFile);
			rolation = exif.getAttribute(ExifInterface.TAG_ORIENTATION);
			Log.d("TAG_ORIENTATION", rolation);
		}
		catch (IOException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		Bitmap _sourceBitmap = decodeBitmapFromFile(pathFile, reqWidth, reqHeight);
		if (!rolation.equals("1"))
		{
			if (rolation.equals("6"))
			{
				Bitmap rolationBitmap = rolateBitmap(mContext, _sourceBitmap);
				_sourceBitmap.recycle();
				_sourceBitmap = rolationBitmap;
			}
			if (rolation.equals("3"))
			{
				Bitmap rolationBitmap = rolateBitmap(mContext, _sourceBitmap);
				Bitmap rolationBitmapNext = rolateBitmap(mContext, rolationBitmap);
				rolationBitmap.recycle();
				_sourceBitmap.recycle();
				_sourceBitmap = rolationBitmapNext;
			}
		}
		return _sourceBitmap;
	}

	public static boolean cancelPotentialWork(String fileName, ImageView imageView)
	{
		final BitmapWorkerTask task = getBitmapWorkerTask(imageView);
		if (task != null)
		{
			final String fileNameOfTask = task.fileName;
			if (fileNameOfTask == null)
			{
				task.cancel(true);
			}
			else if (fileNameOfTask.equals(fileName))
			{
				// The same work is already in progress
				return false;
			}
			else
			{
				// Cancel previous task
				task.cancel(true);
			}
		}
		// No task associated with the ImageView, or an existing task was cancelled
		return true;
	}

	public static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView)
	{
		if (imageView != null)
		{
			final Drawable drawable = imageView.getDrawable();
			if (drawable instanceof AsyncDrawable)
			{
				final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
				return asyncDrawable.getBitmapWorkerTask();
			}
		}
		return null;
	}

	static class AsyncDrawable extends BitmapDrawable
	{
		private final WeakReference bitmapWorkerTaskReference;
		public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask)
		{
			super(res, bitmap);
			bitmapWorkerTaskReference = new WeakReference(bitmapWorkerTask);
		}

		public BitmapWorkerTask getBitmapWorkerTask()
		{
			return (BitmapWorkerTask) bitmapWorkerTaskReference.get();
		}
	}
}
